// -------------------------------------------------
// BlinKit - BlinKit Library
// -------------------------------------------------
//   File Name: loader_task.cpp
// Description: LoaderTask Class
//      Author: Ziming Li
//     Created: 2019-03-13
// -------------------------------------------------
// Copyright (C) 2019 MingYang Software Technology.
// -------------------------------------------------

#include "./loader_task.h"

#include "blinkit/blink/public/platform/WebTaskRunner.h"
#include "blinkit/blink/public/platform/WebTraceLocation.h"
#include "blinkit/blink/public/platform/WebURLLoaderClient.h"
#include "blinkit/blink/renderer/platform/network/resource_error.h"
#include "blinkit/blink/renderer/platform/network/ResourceRequest.h"
#include "blinkit/blink/renderer/platform/network/ResourceResponse.h"
#include "blinkit/blink/renderer/wtf/MainThread.h"
#include "third_party/zed/include/zed/net/http_constants.hpp"
#ifdef BLINKIT_UI_ENABLED
#   include "blinkit/blink/renderer/platform/MIMETypeRegistry.h"
#endif

namespace BlinKit {

LoaderTask::LoaderTask(WebURLLoader *loader, const std::shared_ptr<WebTaskRunner> &taskRunner, WebURLLoaderClient *client)
    : m_clientGuard(*client->ObjectForGC()), m_loader(loader), m_taskRunner(taskRunner), m_client(client)
{
}

LoaderTask::~LoaderTask(void) = default;

ResourceError LoaderTask::BuildError(int code, const zed::url &uri)
{
    String domain = String::fromStdUTF8(uri.get_host());
    return ResourceError(domain, code, String::fromStdUTF8(uri.spec()), emptyString());
}

void LoaderTask::CommitResponse(const ResourceResponse &response, const std::string_view &body)
{
    using namespace zed::http;

    const int statusCode = response.httpStatusCode();
    if (statusCode >= status_code::BadRequest && !ShouldIgnoreHTTPStatusCodeErrors())
    {
        int error;
        switch (statusCode)
        {
            case status_code::Forbidden:
                error = BK_ERR_FORBIDDEN;
                break;
            case status_code::NotFound:
                error = BK_ERR_NOT_FOUND;
                break;
            default:
                error = BK_ERR_UNKNOWN;
        }
        m_client->didFail(BuildError(error, URI()));
        return;
    }

    m_client->didReceiveResponse(m_loader, response);
    m_client->didReceiveData(m_loader, body.data(), body.length(), 0);
    m_client->didFinishLoading(m_loader, body.length());
}

void LoaderTask::Finish(int code)
{
    auto callback = [this, code] {
        if (BK_ERR_SUCCESS == code)
        {
            const KURL &uri = URI();

            ResourceResponse response;
            response.setURL(uri);
            std::string_view body;
            int r = PopulateResponse(response, body);
            if (BK_ERR_CANCELLED == r)
                return; // The task will process the response itself.

            if (BK_ERR_SUCCESS == r)
                CommitResponse(response, body);
            else
                m_client->didFail(BuildError(r, uri));
        }
        else
        {
            ReportError(m_client, m_taskRunner.get(), code, URI());
        }
        delete this;
    };
    m_taskRunner->postTask(BLINK_FROM_HERE, std::move(callback));
}

void LoaderTask::ReportError(WebURLLoaderClient *client, WebTaskRunner *taskRunner, int errorCode, const zed::url &URL)
{
    taskRunner->postTask(BLINK_FROM_HERE, [error = BuildError(errorCode, URL), client] {
        GCGuard _(*client->ObjectForGC());
        client->didFail(error);
    });
}

#ifdef BLINKIT_UI_ENABLED
LoaderTaskForUI::LoaderTaskForUI(const ResourceRequest &request, blink::WebURLLoader *loader, const std::shared_ptr<WebTaskRunner> &taskRunner, WebURLLoaderClient *client)
    : LoaderTask(loader, taskRunner, client)
    , m_URI(request.url())
{
}

AtomicString LoaderTaskForUI::MIMEType(void) const
{
    const std::string fileName = m_URI.ExtractFileName();

    size_t p = fileName.rfind('.');
    if (std::string::npos == p)
        return AtomicString::fromUTF8("application/x-unknown-content-type");

    String ret = MIMETypeRegistry::getMIMETypeForExtension(String::fromUTF8(fileName.data() + p + 1));
    return AtomicString(ret);
}

void LoaderTaskForUI::run(void)
{
    ASSERT(!isMainThread());

    int r = PerformRequest();
    Finish(r);
}
#endif

} // namespace BlinKit
